package com.borya.util;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

public class StringUtil {

	static Logger log = Logger.getLogger(StringUtil.class);

	// Constants used by escapeHTMLTags
	private static final char[] QUOTE_ENCODE = "&quot;".toCharArray();
	private static final char[] AMP_ENCODE = "&amp;".toCharArray();
	private static final char[] LT_ENCODE = "&lt;".toCharArray();
	private static final char[] GT_ENCODE = "&gt;".toCharArray();

	private static final String EMPTY_STRING = "";

	private StringUtil() {
		// Not instantiable.
	}

	private static Pattern p = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$"); // 验证手机号  
	public static boolean check_phone(String phone) {
		if(phone.trim().length() != 11){
			log.info(phone + "不是一个合法的手机号码");
			return false;
		}
		
		return p.matcher(phone).matches();   
	}
	
	/**
	 * 给每个id加上'',如[1,2,3]->'1','2','3'
	 * @param idList
	 * @return
	 */
	public static String decorateIds(List<String> idList){
		if(isNULL(idList)){
			return "";
		}
		if(idList.size()>0){
			String resultStr = "";
			for (int index = 0, length = idList.size(); index < length; index++) {
				resultStr = resultStr + "'" + idList.get(index) + "',";
			}
			return resultStr.substring(0, resultStr.length() - 1);
		}
		return "";
	}
	
	/**
	 * 给每个id加上'',如"1,2,3"->'1','2','3'
	 * @param ids
	 * @return
	 */
	public static String decorateIds(String ids){
		if(isNULL(ids)){
			return "";
		}
		String[] array = ids.split(",");
		if(array.length>0){
			StringBuilder sb = new StringBuilder();
			int i = 0;
			for(String id :array){
				if(i!= 0){sb.append(",");}
				sb.append("'"+id+"'");
				i++;
			}
			return sb.toString();
		}
		return "";
	}
	
	/**
	 * 给每个id加上'',如"1,2,3"->"1","2","3"
	 * @param ids
	 * @return
	 */
	public static String decorateIDs(String ids){
		if(isNULL(ids)){
			return "";
		}
		String[] array = ids.split(",");
		if(array.length>0){
			String resultStr = "";
			for (int index = 0, length = array.length; index < length; index++) {
				resultStr = resultStr + "\"" + array[index] + "\",";
			}
			return resultStr.substring(0, resultStr.length() - 1);
		}
		return "";
	}
	
	public static boolean hasText(String str) {
		if (!hasLength(str)) {
			return false;
		}
		int strLen = str.length();
		for (int i = 0; i < strLen; i++) {
			if (!Character.isWhitespace(str.charAt(i))) {
				return true;
			}
		}
		return false;
	}

	public static boolean hasLength(String str) {
		return (str != null && str.length() > 0);
	}

	/*
	 * public static boolean isEmpty(String str){ if(str == null ||
	 * "".equals(str)){ return false; } return true; }
	 */
	public static boolean isNULL(String str) {
		if (str == null || "".equals(str.trim())) {
			return true;
		}
		return false;
	}

	public static boolean isNULL(Object obj) {
		if (obj == null || "".equals(obj.toString().trim())) {
			return true;
		}
		return false;
	}

	public static boolean isNULL(List<?> list) {
		if (list == null || list.size() < 1) {
			return true;
		}
		return false;
	}

	public static String clean(String in) {
		String out = in;

		if (in != null) {
			out = in.trim();
			if (out.equals(EMPTY_STRING)) {
				out = null;
			}
		}

		return out;
	}

	/**
	 * Replaces all instances of oldString with newString in string.
	 * 
	 * @param string
	 *            the String to search to perform replacements on.
	 * @param oldString
	 *            the String that should be replaced by newString.
	 * @param newString
	 *            the String that will replace all instances of oldString.
	 * @return a String will all instances of oldString replaced by newString.
	 */
	public static String replace(String string, String oldString,
			String newString) {
		if (string == null) {
			return null;
		}
		int i = 0;
		// Make sure that oldString appears at least once before doing any
		// processing.
		if ((i = string.indexOf(oldString, i)) >= 0) {
			// Use char []'s, as they are more efficient to deal with.
			char[] string2 = string.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(string2.length);
			buf.append(string2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			// Replace all remaining instances of oldString with newString.
			while ((i = string.indexOf(oldString, i)) > 0) {
				buf.append(string2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(string2, j, string2.length - j);
			return buf.toString();
		}
		return string;
	}

	/**
	 * Replaces all instances of oldString with newString in line with the added
	 * feature that matches of newString in oldString ignore case.
	 * 
	 * @param line
	 *            the String to search to perform replacements on
	 * @param oldString
	 *            the String that should be replaced by newString
	 * @param newString
	 *            the String that will replace all instances of oldString
	 * @return a String will all instances of oldString replaced by newString
	 */
	public static String replaceIgnoreCase(String line, String oldString,
			String newString) {
		if (line == null) {
			return null;
		}
		String lcLine = line.toLowerCase();
		String lcOldString = oldString.toLowerCase();
		int i = 0;
		if ((i = lcLine.indexOf(lcOldString, i)) >= 0) {
			char[] line2 = line.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(line2.length);
			buf.append(line2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			while ((i = lcLine.indexOf(lcOldString, i)) > 0) {
				buf.append(line2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(line2, j, line2.length - j);
			return buf.toString();
		}
		return line;
	}

	/**
	 * Replaces all instances of oldString with newString in line with the added
	 * feature that matches of newString in oldString ignore case. The count
	 * paramater is set to the number of replaces performed.
	 * 
	 * @param line
	 *            the String to search to perform replacements on
	 * @param oldString
	 *            the String that should be replaced by newString
	 * @param newString
	 *            the String that will replace all instances of oldString
	 * @param count
	 *            a value that will be updated with the number of replaces
	 *            performed.
	 * @return a String will all instances of oldString replaced by newString
	 */
	public static String replaceIgnoreCase(String line, String oldString,
			String newString, int[] count) {
		if (line == null) {
			return null;
		}
		String lcLine = line.toLowerCase();
		String lcOldString = oldString.toLowerCase();
		int i = 0;
		if ((i = lcLine.indexOf(lcOldString, i)) >= 0) {
			int counter = 1;
			char[] line2 = line.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(line2.length);
			buf.append(line2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			while ((i = lcLine.indexOf(lcOldString, i)) > 0) {
				counter++;
				buf.append(line2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(line2, j, line2.length - j);
			count[0] = counter;
			return buf.toString();
		}
		return line;
	}

	/**
	 * Replaces all instances of oldString with newString in line. The count
	 * Integer is updated with number of replaces.
	 * 
	 * @param line
	 *            the String to search to perform replacements on.
	 * @param oldString
	 *            the String that should be replaced by newString.
	 * @param newString
	 *            the String that will replace all instances of oldString.
	 * @return a String will all instances of oldString replaced by newString.
	 */
	public static String replace(String line, String oldString,
			String newString, int[] count) {
		if (line == null) {
			return null;
		}
		int i = 0;
		if ((i = line.indexOf(oldString, i)) >= 0) {
			int counter = 1;
			char[] line2 = line.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(line2.length);
			buf.append(line2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			while ((i = line.indexOf(oldString, i)) > 0) {
				counter++;
				buf.append(line2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(line2, j, line2.length - j);
			count[0] = counter;
			return buf.toString();
		}
		return line;
	}

	/**
	 * This method takes a string and strips out all tags except <br>
	 * tags while still leaving the tag body intact.
	 * 
	 * @param in
	 *            the text to be converted.
	 * @return the input string with all tags removed.
	 */
	public static String stripTags(String in) {
		if (in == null) {
			return null;
		}
		char ch;
		int i = 0;
		int last = 0;
		char[] input = in.toCharArray();
		int len = input.length;
		StringBuilder out = new StringBuilder((int) (len * 1.3));
		for (; i < len; i++) {
			ch = input[i];
			if (ch > '>') {
			} else if (ch == '<') {
				if (i + 3 < len && input[i + 1] == 'b' && input[i + 2] == 'r'
						&& input[i + 3] == '>') {
					i += 3;
					continue;
				}
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
			} else if (ch == '>') {
				last = i + 1;
			}
		}
		if (last == 0) {
			return in;
		}
		if (i > last) {
			out.append(input, last, i - last);
		}
		return out.toString();
	}

	/**
	 * This method takes a string which may contain HTML tags (ie, &lt;b&gt;,
	 * &lt;table&gt;, etc) and converts the '&lt'' and '&gt;' characters to
	 * their HTML escape sequences. It will also replace LF with &lt;br&gt;.
	 * 
	 * @param in
	 *            the text to be converted.
	 * @return the input string with the characters '&lt;' and '&gt;' replaced
	 *         with their HTML escape sequences.
	 */
	public static String escapeHTMLTags(String in) {
		if (in == null) {
			return null;
		}
		char ch;
		int i = 0;
		int last = 0;
		char[] input = in.toCharArray();
		int len = input.length;
		StringBuilder out = new StringBuilder((int) (len * 1.3));
		for (; i < len; i++) {
			ch = input[i];
			if (ch > '>') {
			} else if (ch == '<') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(LT_ENCODE);
			} else if (ch == '>') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(GT_ENCODE);
			} else if (ch == '\n') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append("<br>");
			}
		}
		if (last == 0) {
			return in;
		}
		if (i > last) {
			out.append(input, last, i - last);
		}
		return out.toString();
	}

	/**
	 * Used by the hash method.
	 */
	private static Map<String, MessageDigest> digests = new ConcurrentHashMap<String, MessageDigest>();

	/**
	 * Hashes a String using the Md5 algorithm and returns the result as a
	 * String of hexadecimal numbers. This method is synchronized to avoid
	 * excessive MessageDigest object creation. If calling this method becomes a
	 * bottleneck in your code, you may wish to maintain a pool of MessageDigest
	 * objects instead of using this method.
	 * <p/>
	 * A hash is a one-way function -- that is, given an input, an output is
	 * easily computed. However, given the output, the input is almost
	 * impossible to compute. This is useful for passwords since we can store
	 * the hash and a hacker will then have a very hard time determining the
	 * original password.
	 * <p/>
	 * In Jive, every time a user loggers in, we simply take their plain text
	 * password, compute the hash, and compare the generated hash to the stored
	 * hash. Since it is almost impossible that two passwords will generate the
	 * same hash, we know if the user gave us the correct password or not. The
	 * only negative to this system is that password recovery is basically
	 * impossible. Therefore, a reset password method is used instead.
	 * 
	 * @param data
	 *            the String to compute the hash of.
	 * @return a hashed version of the passed-in String
	 */
	public static String hash(String data) {
		return hash(data, "MD5");
	}

	/**
	 * Hashes a String using the specified algorithm and returns the result as a
	 * String of hexadecimal numbers. This method is synchronized to avoid
	 * excessive MessageDigest object creation. If calling this method becomes a
	 * bottleneck in your code, you may wish to maintain a pool of MessageDigest
	 * objects instead of using this method.
	 * <p/>
	 * A hash is a one-way function -- that is, given an input, an output is
	 * easily computed. However, given the output, the input is almost
	 * impossible to compute. This is useful for passwords since we can store
	 * the hash and a hacker will then have a very hard time determining the
	 * original password.
	 * <p/>
	 * In Jive, every time a user loggers in, we simply take their plain text
	 * password, compute the hash, and compare the generated hash to the stored
	 * hash. Since it is almost impossible that two passwords will generate the
	 * same hash, we know if the user gave us the correct password or not. The
	 * only negative to this system is that password recovery is basically
	 * impossible. Therefore, a reset password method is used instead.
	 * 
	 * @param data
	 *            the String to compute the hash of.
	 * @param algorithm
	 *            the name of the algorithm requested.
	 * @return a hashed version of the passed-in String
	 */
	public static String hash(String data, String algorithm) {
		try {
			return hash(data.getBytes("UTF-8"), algorithm);
		} catch (UnsupportedEncodingException e) {
			log.error(e.getMessage());
		}
		return data;
	}

	/**
	 * Hashes a byte array using the specified algorithm and returns the result
	 * as a String of hexadecimal numbers. This method is synchronized to avoid
	 * excessive MessageDigest object creation. If calling this method becomes a
	 * bottleneck in your code, you may wish to maintain a pool of MessageDigest
	 * objects instead of using this method.
	 * <p/>
	 * A hash is a one-way function -- that is, given an input, an output is
	 * easily computed. However, given the output, the input is almost
	 * impossible to compute. This is useful for passwords since we can store
	 * the hash and a hacker will then have a very hard time determining the
	 * original password.
	 * <p/>
	 * In Jive, every time a user loggers in, we simply take their plain text
	 * password, compute the hash, and compare the generated hash to the stored
	 * hash. Since it is almost impossible that two passwords will generate the
	 * same hash, we know if the user gave us the correct password or not. The
	 * only negative to this system is that password recovery is basically
	 * impossible. Therefore, a reset password method is used instead.
	 * 
	 * @param bytes
	 *            the byte array to compute the hash of.
	 * @param algorithm
	 *            the name of the algorithm requested.
	 * @return a hashed version of the passed-in String
	 */
	public static String hash(byte[] bytes, String algorithm) {
		synchronized (algorithm.intern()) {
			MessageDigest digest = digests.get(algorithm);
			if (digest == null) {
				try {
					digest = MessageDigest.getInstance(algorithm);
					digests.put(algorithm, digest);
				} catch (NoSuchAlgorithmException nsae) {
					log.error("Failed to load the " + algorithm
							+ " MessageDigest. "
							+ "Jive will be unable to function normally.", nsae);
					return null;
				}
			}
			// Now, compute hash.
			digest.update(bytes);
			return encodeHex(digest.digest());
		}
	}

	/**
	 * Turns an array of bytes into a String representing each byte as an
	 * unsigned hex number.
	 * <p/>
	 * Method by Santeri Paavolainen, Helsinki Finland 1996<br>
	 * (c) Santeri Paavolainen, Helsinki Finland 1996<br>
	 * Distributed under LGPL.
	 * 
	 * @param bytes
	 *            an array of bytes to convert to a hex-string
	 * @return generated hex string
	 */
	public static String encodeHex(byte[] bytes) {
		StringBuilder buf = new StringBuilder(bytes.length * 2);
		int i;

		for (i = 0; i < bytes.length; i++) {
			if (((int) bytes[i] & 0xff) < 0x10) {
				buf.append("0");
			}
			buf.append(Long.toString((int) bytes[i] & 0xff, 16));
		}
		return buf.toString();
	}

	/**
	 * Turns a hex encoded string into a byte array. It is specifically meant to
	 * "reverse" the toHex(byte[]) method.
	 * 
	 * @param hex
	 *            a hex encoded String to transform into a byte array.
	 * @return a byte array representing the hex String[
	 */
	public static byte[] decodeHex(String hex) {
		char[] chars = hex.toCharArray();
		byte[] bytes = new byte[chars.length / 2];
		int byteCount = 0;
		for (int i = 0; i < chars.length; i += 2) {
			int newByte = 0x00;
			newByte |= hexCharToByte(chars[i]);
			newByte <<= 4;
			newByte |= hexCharToByte(chars[i + 1]);
			bytes[byteCount] = (byte) newByte;
			byteCount++;
		}
		return bytes;
	}

	/**
	 * Returns the the byte value of a hexadecmical char (0-f). It's assumed
	 * that the hexidecimal chars are lower case as appropriate.
	 * 
	 * @param ch
	 *            a hexedicmal character (0-f)
	 * @return the byte value of the character (0x00-0x0F)
	 */
	private static byte hexCharToByte(char ch) {
		switch (ch) {
		case '0':
			return 0x00;
		case '1':
			return 0x01;
		case '2':
			return 0x02;
		case '3':
			return 0x03;
		case '4':
			return 0x04;
		case '5':
			return 0x05;
		case '6':
			return 0x06;
		case '7':
			return 0x07;
		case '8':
			return 0x08;
		case '9':
			return 0x09;
		case 'a':
			return 0x0A;
		case 'b':
			return 0x0B;
		case 'c':
			return 0x0C;
		case 'd':
			return 0x0D;
		case 'e':
			return 0x0E;
		case 'f':
			return 0x0F;
		}
		return 0x00;
	}

	 
	/**
	 * Converts a line of text into an array of lower case words using a
	 * BreakIterator.wordInstance().
	 * <p>
	 * 
	 * This method is under the Jive Open Source Software License and was
	 * written by Mark Imbriaco.
	 * 
	 * @param text
	 *            a String of text to convert into an array of words
	 * @return text broken up into an array of words.
	 */
	public static String[] toLowerCaseWordArray(String text) {
		if (text == null || text.length() == 0) {
			return new String[0];
		}

		List<String> wordList = new ArrayList<String>();
		BreakIterator boundary = BreakIterator.getWordInstance();
		boundary.setText(text);
		int start = 0;

		for (int end = boundary.next(); end != BreakIterator.DONE; start = end, end = boundary
				.next()) {
			String tmp = text.substring(start, end).trim();
			// Remove characters that are not needed.
			tmp = replace(tmp, "+", "");
			tmp = replace(tmp, "/", "");
			tmp = replace(tmp, "\\", "");
			tmp = replace(tmp, "#", "");
			tmp = replace(tmp, "*", "");
			tmp = replace(tmp, ")", "");
			tmp = replace(tmp, "(", "");
			tmp = replace(tmp, "&", "");
			if (tmp.length() > 0) {
				wordList.add(tmp);
			}
		}
		return wordList.toArray(new String[wordList.size()]);
	}

	/**
	 * Pseudo-random number generator object for use with randomString(). The
	 * Random class is not considered to be cryptographically secure, so only
	 * use these random Strings for low to medium security applications.
	 */
	private static Random randGen = new Random();

	/**
	 * Array of numbers and letters of mixed case. Numbers appear in the list
	 * twice so that there is a more equal chance that a number will be picked.
	 * We can use the array to get a random number or letter by picking a random
	 * array index.
	 */
	private static char[] numbersAndLetters = "0123456789abcdefghijklmnopqrstuvwxyz"
			.toCharArray();

	/**
	 * Returns a random String of numbers and letters (lower and upper case) of
	 * the specified length. The method uses the Random class that is built-in
	 * to Java which is suitable for low to medium grade security uses. This
	 * means that the output is only pseudo random, i.e., each number is
	 * mathematically generated so is not truly random.
	 * <p>
	 * <p/>
	 * The specified length must be at least one. If not, the method will return
	 * null.
	 * 
	 * @param length
	 *            the desired length of the random String to return.
	 * @return a random String of numbers and letters of the specified length.
	 */
	public static String randomString(int length) {
		if (length < 1) {
			return null;
		}
		// Create a char buffer to put random letters and numbers in.
		char[] randBuffer = new char[length];
		for (int i = 0; i < randBuffer.length; i++) {
			randBuffer[i] = numbersAndLetters[randGen.nextInt(35)];
		}
		return new String(randBuffer);
	}

	/**
	 * Intelligently chops a String at a word boundary (whitespace) that occurs
	 * at the specified index in the argument or before. However, if there is a
	 * newline character before <code>length</code>, the String will be chopped
	 * there. If no newline or whitespace is found in <code>string</code> up to
	 * the index <code>length</code>, the String will chopped at
	 * <code>length</code>.
	 * <p/>
	 * For example, chopAtWord("This is a nice String", 10) will return
	 * "This is a" which is the first word boundary less than or equal to 10
	 * characters into the original String.
	 * 
	 * @param string
	 *            the String to chop.
	 * @param length
	 *            the index in <code>string</code> to start looking for a
	 *            whitespace boundary at.
	 * @return a substring of <code>string</code> whose length is less than or
	 *         equal to <code>length</code>, and that is chopped at whitespace.
	 */
	public static String chopAtWord(String string, int length) {
		if (string == null || string.length() == 0) {
			return string;
		}

		char[] charArray = string.toCharArray();
		int sLength = string.length();
		if (length < sLength) {
			sLength = length;
		}

		// First check if there is a newline character before length; if so,
		// chop word there.
		for (int i = 0; i < sLength - 1; i++) {
			// Windows
			if (charArray[i] == '\r' && charArray[i + 1] == '\n') {
				return string.substring(0, i + 1);
			}
			// Unix
			else if (charArray[i] == '\n') {
				return string.substring(0, i);
			}
		}
		// Also check boundary case of Unix newline
		if (charArray[sLength - 1] == '\n') {
			return string.substring(0, sLength - 1);
		}

		// Done checking for newline, now see if the total string is less than
		// the specified chop point.
		if (string.length() < length) {
			return string;
		}

		// No newline, so chop at the first whitespace.
		for (int i = length - 1; i > 0; i--) {
			if (charArray[i] == ' ') {
				return string.substring(0, i).trim();
			}
		}

		// Did not find word boundary so return original String chopped at
		// specified length.
		return string.substring(0, length);
	}

	/**
	 * Escapes all necessary characters in the String so that it can be used in
	 * SQL
	 * 
	 * @param string
	 *            the string to escape.
	 * @return the string with appropriate characters escaped.
	 */
	public static String escapeForSQL(String string) {
		if (string == null) {
			return null;
		} else if (string.length() == 0) {
			return string;
		}

		char ch;
		char[] input = string.toCharArray();
		int i = 0;
		int last = 0;
		int len = input.length;
		StringBuilder out = null;
		for (; i < len; i++) {
			ch = input[i];

			if (ch == '\'') {
				if (out == null) {
					out = new StringBuilder(len + 2);
				}
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append('\'').append('\'');
			}
		}

		if (out == null) {
			return string;
		} else if (i > last) {
			out.append(input, last, i - last);
		}

		return out.toString();
	}

	/**
	 * Escapes all necessary characters in the String so that it can be used in
	 * an XML doc.
	 * 
	 * @param string
	 *            the string to escape.
	 * @return the string with appropriate characters escaped.
	 */
	public static String escapeForXML(String string) {
		if (string == null) {
			return null;
		}
		char ch;
		int i = 0;
		int last = 0;
		char[] input = string.toCharArray();
		int len = input.length;
		StringBuilder out = new StringBuilder((int) (len * 1.3));
		for (; i < len; i++) {
			ch = input[i];
			if (ch > '>') {
			} else if (ch == '<') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(LT_ENCODE);
			} else if (ch == '&') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(AMP_ENCODE);
			} else if (ch == '"') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(QUOTE_ENCODE);
			}
		}
		if (last == 0) {
			return string;
		}
		if (i > last) {
			out.append(input, last, i - last);
		}
		return out.toString();
	}

	/**
	 * Unescapes the String by converting XML escape sequences back into normal
	 * characters.
	 * 
	 * @param string
	 *            the string to unescape.
	 * @return the string with appropriate characters unescaped.
	 */
	public static String unescapeFromXML(String string) {
		string = replace(string, "&lt;", "<");
		string = replace(string, "&gt;", ">");
		string = replace(string, "&quot;", "\"");
		return replace(string, "&amp;", "&");
	}

	private static final char[] zeroArray = "0000000000000000000000000000000000000000000000000000000000000000"
			.toCharArray();

	/**
	 * Pads the supplied String with 0's to the specified length and returns the
	 * result as a new String. For example, if the initial String is "9999" and
	 * the desired length is 8, the result would be "00009999". This type of
	 * padding is useful for creating numerical values that need to be stored
	 * and sorted as character data. Note: the current implementation of this
	 * method allows for a maximum <tt>length</tt> of 64.
	 * 
	 * @param string
	 *            the original String to pad.
	 * @param length
	 *            the desired length of the new padded String.
	 * @return a new String padded with the required number of 0's.
	 */
	public static String zeroPadString(String string, int length) {
		if (string == null || string.length() > length) {
			return string;
		}
		StringBuilder buf = new StringBuilder(length);
		buf.append(zeroArray, 0, length - string.length()).append(string);
		return buf.toString();
	}

	/**
	 * Formats a Date as a fifteen character long String made up of the Date's
	 * padded millisecond value.
	 * 
	 * @return a Date encoded as a String.
	 */
	public static String dateToMillis(Date date) {
		return zeroPadString(Long.toString(date.getTime()), 15);
	}

	/**
	 * Returns a formatted String from time.
	 * 
	 * @param diff
	 *            the amount of elapsed time.
	 * @return the formatte String.
	 */
	public static String getTimeFromLong(long diff) {
		final String HOURS = "h";
		final String MINUTES = "min";
		// final String SECONDS = "sec";

		final long MS_IN_A_DAY = 1000 * 60 * 60 * 24;
		final long MS_IN_AN_HOUR = 1000 * 60 * 60;
		final long MS_IN_A_MINUTE = 1000 * 60;
		final long MS_IN_A_SECOND = 1000;
		// Date currentTime = new Date();
		// long numDays = diff / MS_IN_A_DAY;
		diff = diff % MS_IN_A_DAY;
		long numHours = diff / MS_IN_AN_HOUR;
		diff = diff % MS_IN_AN_HOUR;
		long numMinutes = diff / MS_IN_A_MINUTE;
		diff = diff % MS_IN_A_MINUTE;
		// long numSeconds = diff / MS_IN_A_SECOND;
		diff = diff % MS_IN_A_SECOND;
		// long numMilliseconds = diff;

		StringBuffer buf = new StringBuffer();
		if (numHours > 0) {
			buf.append(numHours + " " + HOURS + ", ");
		}

		if (numMinutes > 0) {
			buf.append(numMinutes + " " + MINUTES);
		}

		// buf.append(numSeconds + " " + SECONDS);

		String result = buf.toString();

		if (numMinutes < 1) {
			result = "< 1 minute";
		}

		return result;
	}

	/**
	 * Returns a collection of Strings as a comma-delimitted list of strings.
	 * 
	 * @return a String representing the Collection.
	 */
	public static String collectionToString(Collection<String> collection) {
		if (collection == null || collection.isEmpty()) {
			return "";
		}
		StringBuilder buf = new StringBuilder();
		String delim = "";
		for (String element : collection) {
			buf.append(delim);
			buf.append(element);
			delim = ",";
		}
		return buf.toString();
	}

	/**
	 * Returns a comma-delimitted list of Strings as a Collection.
	 * 
	 * @return a Collection representing the String.
	 */
	public static Collection<String> stringToCollection(String string) {
		if (string == null || string.trim().length() == 0) {
			return Collections.emptyList();
		}
		Collection<String> collection = new ArrayList<String>();
		StringTokenizer tokens = new StringTokenizer(string, ",");
		while (tokens.hasMoreTokens()) {
			collection.add(tokens.nextToken().trim());
		}
		return collection;
	}

	/**
	 * Abbreviates a string to a specified length and then adds an ellipsis if
	 * the input is greater than the maxWidth. Example input:
	 * 
	 * <pre>
	 *      user1@jivesoftware.com/home
	 * </pre>
	 * 
	 * and a maximum length of 20 characters, the abbreviate method will return:
	 * 
	 * <pre>
	 *      user1@jivesoftware.c...
	 * </pre>
	 * 
	 * @param str
	 *            the String to abbreviate.
	 * @param maxWidth
	 *            the maximum size of the string, minus the ellipsis.
	 * @return the abbreviated String, or <tt>null</tt> if the string was
	 *         <tt>null</tt>.
	 */
	public static String abbreviate(String str, int maxWidth) {
		if (null == str) {
			return null;
		}

		if (str.length() <= maxWidth) {
			return str;
		}

		return str.substring(0, maxWidth) + "...";
	}

	/**
	 * Removes characters likely to enable Cross Site Scripting attacks from the
	 * provided input string. The characters that are removed from the input
	 * string, if present, are:
	 * 
	 * <pre>
	 * &lt; &gt; &quot; ' % ; ) ( &amp; + -
	 * </pre>
	 * 
	 * @param input
	 * @return Input without certain characters;
	 */
	public static String removeXSSCharacters(String input) {
		final String[] xss = { "<", ">", "\"", "'", "%", ";", ")", "(", "&",
				"+", "-" };
		for (int i = 0; i < xss.length; i++) {
			input = input.replace(xss[i], "");
		}
		return input;
	}
	
	/*
	 * 判断字符串中是否含有中文
	 */
	public static boolean isContainedChinese(String str) {
		int n = 0;
		boolean result = false;
		for (int i = 0; i < str.length(); i++) {
			n = (int) str.charAt(i);
			if ((19968 <= n && n < 40623)) {
				result = true;
				break;
			}
		}
		return result;
	}

	/***
	 * 去掉字符集合两端的[ ]
	 * 
	 * @param arrayStr
	 * @return
	 * @throws Exception
	 */
	public static String removeSquareBrackets(String arrayStr) throws Exception {

		String aryStr = arrayStr;
		boolean startflag = aryStr.startsWith("[");
		boolean endflag = aryStr.endsWith("]");
		if (startflag) {
			aryStr = aryStr.substring(1);
		} else {
			throw new Exception();
		}
		if (endflag) {
			aryStr = aryStr.substring(0, aryStr.length() - 1);
		}
		return aryStr;
	}

	public static String replaces(String from, String to, String source) {
		if (source == null || from == null || to == null)
			return null;
		StringBuffer bf = new StringBuffer("");
		int index = -1;
		while ((index = source.indexOf(from)) != -1) {
			bf.append(source.substring(0, index) + to);
			source = source.substring(index + from.length());
			index = source.indexOf(from);
		}
		bf.append(source);
		return bf.toString();
	}

	public static String randomNumberString(int length) {
		char[] numbersAndLetters = ("0123456789").toCharArray();
		if (length < 1) {
			return null;
		}
		// Create a char buffer to put random letters and numbers in.
		char[] randBuffer = new char[length];
		for (int i = 0; i < length; i++) {
			randBuffer[i] = numbersAndLetters[randGen.nextInt(9)];
		}
		return new String(randBuffer);
	}

	public static String addapostrophes(String sourceStr) {
		if (StringUtil.isNULL(sourceStr)) {
			return "";
		} else {
			return "'" + StringUtil.replaces(",", "','", sourceStr) + "'";
		}
	}

	public static String UUID() {
		return java.util.UUID.randomUUID().toString();
	}

	/**
	 * Test if the given String starts with the specified prefix, ignoring
	 * upper/lower case.
	 * <p/>
	 * <p>
	 * Copied from the Spring Framework while retaining all license, copyright
	 * and author information.
	 * 
	 * @param str
	 *            the String to check
	 * @param prefix
	 *            the prefix to look for
	 * @return <code>true</code> starts with the specified prefix (ignoring
	 *         case), <code>false</code> if it does not.
	 * @see java.lang.String#startsWith
	 */
	public static boolean startsWithIgnoreCase(String str, String prefix) {
		if (str == null || prefix == null) {
			return false;
		}
		if (str.startsWith(prefix)) {
			return true;
		}
		if (str.length() < prefix.length()) {
			return false;
		}
		String lcStr = str.substring(0, prefix.length()).toLowerCase();
		String lcPrefix = prefix.toLowerCase();
		return lcStr.equals(lcPrefix);
	}

	/*
	 * 将以逗号分隔的字符串转为List<String>格式使用
	 * @param str
	 * 这个地方会抛异常出去,让上层来接收这个异常 
	 */
	public static List<String> stringConvertList(String str) {
		List<String> dstList = new ArrayList<String>();
		String[] strArray = str.split(",");
		for (int index = 0; index < strArray.length; index++) {
			String obj = strArray[index];
			dstList.add(obj);
		}
		return dstList;
	}

	public static String getSysOrderId(String prifix, Long nowTime) {
		final String tDateStr = DateUtils.getDetailTimeOrderId(nowTime);
		String s = String.valueOf(StringUtil.ato.getAndAdd(1));
		switch (s.length()) {
		case 1:
			return prifix + tDateStr + "0000" + s;
		case 2:
			return prifix + tDateStr + "000" + s;
		case 3:
			return prifix + tDateStr + "00" + s;
		case 4:
			return prifix + tDateStr + "0" + s;
		default:
			return prifix + tDateStr + s;
		}
	}
	public static AtomicLong ato = new AtomicLong();

	public static String getMsgId(final long curTime){
	    return "MESSAGE_"+DateUtils.format(curTime, "yyMMddHHmm") + randomNumberString(6);
	}


    public static String createSysOrderPayId() {
        return DateUtils.format("yyMMddHHmmssSSS") + randomNumberString(5);
    }
}